#ifndef DW_MATHS_POINT_H
#	define DW_MATHS_POINT_H

#include "maths/vector.h"

namespace dw {
	namespace maths {

template< typename T, unsigned N >
struct Point;

template< typename T, unsigned N >
inline bool operator==(const Point< T, N >& lhs, const Point< T, N >& rhs);

template< typename T, unsigned N >
inline Point< T, N >								operator-(const Point< T, N >& rhs);

template< typename T, unsigned N >
inline Point< T, N >								operator+(const Point< T, N >& lhs, const Point< T, N >& rhs);

template< typename T, unsigned N >
inline Point< T, N >								operator+(const Point< T, N >& lhs, const Vector< T, N >& rhs);

template< typename T, unsigned N >
inline Vector< T, N >								operator-(const Point< T, N >& lhs, const Point< T, N >& rhs);

template< typename T, unsigned N >
inline Point< T, N >								operator*(const Point< T, N >& lhs, const T& rhs);

template< typename T, unsigned N >
inline Point< T, N >								operator*(const T& lhs, const Point< T, N >& rhs);

template< typename T, unsigned N >
inline Point< T, N >								operator/(const Point< T, N >& lhs, const T& rhs);


template< typename T, unsigned N >
struct Point
{
// Fiefds
public:

	T p[N];

// Constructors
public:

	inline 													Point();

	inline 													Point(const T* rhs);

	template< unsigned P >
	inline Point(const Point< T, P >& rhs);


// Methods
public:

	inline const T& operator[](const unsigned i) const { assert(i < N); return p[i]; }

	inline T& operator[](const unsigned i) { assert(i < N); return p[i]; }

	inline Point< T, N >&							operator=(const Point< T, N >& rhs);

	inline Point< T, N >&							operator+=(const Point< T, N >& rhs);

	inline Point< T, N >&							operator+=(const Vector< T, N >& rhs);

	inline Point< T, N >&							operator-=(const Vector< T, N >& rhs);

	inline Point< T, N >&							operator*=(const T& rhs);

	inline Point< T, N >&							operator/=(const T& rhs);



	inline Vector< T, N >							toVector() const ;

	inline std::string toString() const ;
	
};

template< typename T >
struct Point< T, 2 >
{
public:
	
	static const Point< T, 2 >				zero;

// Fields
public:
	
	union
	{
		T p[2];
		struct 
		{
			T x, y;
		};
	};

// Constructors
public:

	inline 													Point();

	inline 													Point(const T* rhs);

	inline 													Point(const T& x, const T& y);


// Methods
public:

	inline const T& operator[](const unsigned i) const { assert(i < 2); return p[i]; }

	inline T& operator[](const unsigned i) { assert(i < 2); return p[i]; }

	inline Point< T, 2 >&							operator=(const Point< T, 2 >& rhs);

	inline Point< T, 2 >&							operator+=(const Point< T, 2 >& rhs);

	inline Point< T, 2 >&							operator+=(const Vector< T, 2 >& rhs);

	inline Point< T, 2 >&							operator-=(const Vector< T, 2 >& rhs);

	inline Point< T, 2 >&							operator*=(const T& rhs);

	inline Point< T, 2 >&							operator/=(const T& rhs);



	inline std::string toString() const ;

};

template< typename T >
struct Point< T, 3 >
{
public:
	
	static const Point< T, 3 >				zero;

// Fields
public:

	union
	{
		T p[3];
		struct 
		{
			T x, y, z;
		};
	};

// Constructor
public:

	inline 													Point();

	inline 													Point(const T* rhs);
	
	inline 													Point(const T& x, const T& y, const T& z);


// Methods
public:

	inline const T& operator[](const unsigned i) const { assert(i < 3); return p[i]; }

	inline T& operator[](const unsigned i) { assert(i < 3); return p[i]; }

	inline Point< T, 3 >&							operator=(const Point< T, 3 >& rhs);

	inline Point< T, 3 >&							operator+=(const Point< T, 3 >& rhs);

	inline Point< T, 3 >&							operator+=(const Vector< T, 3 >& rhs);

	inline Point< T, 3 >&							operator-=(const Vector< T, 3 >& rhs);

	inline Point< T, 3 >&							operator*=(const T& rhs);

	inline Point< T, 3 >&							operator/=(const T& rhs);



	inline std::string toString() const ;



	inline static Point< T, 3 >				transform(const Point< T, 3 >&, const Matrix< T, 4, 4 >& m);

};

template< typename T >
struct Point< T, 4 >
{
public:
	
	static const Point< T, 4 >				zero;

// Fields
public:

	union
	{
		T p[4];
		struct 
		{
			T x, y, z, w;
		};
	};

// Constructors
public:

	inline 													Point();

	inline 													Point(const T* rhs);

	inline 													Point(const T& x, const T& y, const T& z, const T& w);


// Methods
public:

	inline const T& operator[](const unsigned i) const { assert(i < 4); return p[i]; }

	inline T& operator[](const unsigned i) { assert(i < 4); return p[i]; }

	inline Point< T, 4 >&							operator=(const Point< T, 4 >& rhs);

	inline Point< T, 4 >&							operator+=(const Point< T, 4 >& rhs);

	inline Point< T, 4 >&							operator+=(const Vector< T, 4 >& rhs);

	inline Point< T, 4 >&							operator-=(const Vector< T, 4 >& rhs);

	inline Point< T, 4 >&							operator*=(const T& rhs);

	inline Point< T, 4 >&							operator/=(const T& rhs);



	inline std::string toString() const ;

};

typedef Point<dw::uint32, 2>	Point2ui;
typedef Point<dw::uint32, 3>	Point3ui;
typedef Point<dw::uint32, 4>	Point4ui;
typedef Point<dw::int32, 2>		Point2i;
typedef Point<dw::int32, 3>		Point3i;
typedef Point<dw::int32, 4>		Point4i;
typedef Point<dw::float32, 2>	Point2f;
typedef Point<dw::float32, 3>	Point3f;
typedef Point<dw::float32, 4>	Point4f;
typedef Point<dw::float64, 2>	Point2d;
typedef Point<dw::float64, 3>	Point3d;
typedef Point<dw::float64, 4>	Point4d;

	} // namespace maths
} // namespace dw

#include "maths/point.inl"

#endif // !DW_MATHS_POINT_H
